/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package Components;

import Drawable.Polygon;
import Options.Export;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.awt.image.BufferedImage;
import java.util.ArrayList;

/**
 *
 * @author atxy2k
 */
public class CustomCanvas extends Canvas{
    
    private float rWidth, rHeight, pixelSize;
    private int left, right, top, bottom, xmiddle, ymiddle, centerX, centerY;
    private boolean paintGrid = true;
    private ArrayList<Polygon> list = new ArrayList<>();
    private int pos = 0;
    /**
     * 
     * @param rWidth Representa el ancho del plano cartesiano.
     * @param rHeight Representa el alto del plano cartesiano.
     */
    public CustomCanvas(float rWidth, float rHeight) {
        this.rWidth = rWidth;
        this.rHeight = rHeight;
        setBackground(Color.white);
        list.add(new Polygon());
        addActions();
    }
    /**
     *  Constructor vacio, inializa el rWidth en 10 asi como el rHeight.
     */
    public CustomCanvas() {
        this.rWidth = 10.0F;
        this.rHeight = 10.0F;
        setBackground(Color.white);
        list.add(new Polygon());
        addActions();
    }
    /**
     * Agrega las interfaces para manejar el canvas
     */
    private void addActions(){
        addMouseListener(new MouseAdapter() {

            @Override
            public void mouseClicked(MouseEvent e) {
                list.get(pos).add(toRealX(e.getX()), toRealY(e.getY()));
                repaint();
            }

        });
        addMouseWheelListener(new MouseAdapter() {
            
            @Override
            public void mouseWheelMoved(MouseWheelEvent e) {
                changeDrawableSpace(e.getWheelRotation());
            }
            
        });
        addKeyListener(new KeyAdapter() {

            @Override
            public void keyPressed(KeyEvent e) {
                if (e.getKeyCode() == KeyEvent.VK_O) {
                    save();
                }else{
                    list.get(pos).KeyEvent(e);
                }
                repaint();
            }
            
        });
    }
    public void save(){
        Export exp = new Export(this);
        exp.selector("jpeg");
    }
    /**
     * Inicializa los componentes necesarios para un dibujo responsive
     */
    private void initComponents(){
        Dimension d = getSize();
        int maxX = d.width-1;
        int maxY = d.height-1;
        pixelSize = Math.max(rWidth/maxX, rHeight/maxY);
        centerX = maxX/2;
        centerY = maxY/2;
        left = toFisicX(-rWidth/2);
        right = toFisicX(rWidth/2);
        top = toFisicY(rHeight/2);
        bottom = toFisicY(-rHeight/2);
        xmiddle = toFisicX(0);
        ymiddle = toFisicY(0);
    }
    /**
     * 
     * @param g Metodo que pinta el canvas. 
     */
    @Override
    public void paint(Graphics g){
        initComponents();
        paintGrid(g);
        for (int i = 0; i < list.size(); i++) {
            list.get(i).initVars(centerX, centerY, pixelSize);
            list.get(i).paint(g);
        }
    }
    /**
     * 
     * @param g Toma un graphics y dibuja una cuadricula basado en los calculos responsivos
     */
    private void paintGrid(Graphics g2){
        //BufferedImage para pintar la imagen antes de dibujar en el canvas.
        BufferedImage image = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB);
        Graphics g = image.createGraphics();
        g.setColor(Color.white);
        g.fillRect(0, 0, getWidth(), getHeight());
        //Mini grid
        g.setColor(Color.lightGray);
        if (paintGrid) {
            for (float i = (-rWidth/2); i < (rWidth/2); i+=0.1F) {
                g.drawLine(toFisicX(i), top, toFisicX(i), bottom);
            }
            for (float i = (-rHeight/2); i < (rHeight/2); i+=0.1F) {
                g.drawLine(left, toFisicY(i), right, toFisicY(i));
            }
        }
        //Paint grid
        for (int i = Math.round(-rWidth/2); i < (rWidth/2); i++) {
            Color c = paintGrid ? Color.blue : Color.lightGray;
            g.setColor(c);
            g.drawLine(toFisicX(i), top, toFisicX(i), bottom);
            g.setColor(Color.black);
            g.drawString(""+i, toFisicX(i), ymiddle);
        }
        for (int i = Math.round(-rHeight/2); i < (rHeight/2); i++) {
            Color c = paintGrid ? Color.blue : Color.lightGray;
            g.setColor(c);
            g.drawLine(left, toFisicY(i), right, toFisicY(i));
            g.setColor(Color.black);
            g.drawString(""+i, xmiddle, toFisicY(i));
        }
        //Paint Square
        g.drawLine(left, top, right, top);
        g.drawLine(right, top, right, bottom);
        g.drawLine(right, bottom, left, bottom);
        g.drawLine(left, bottom, left, top);
        //Pintando en el canvas como imagen y no como dibujo paso a paso. 
        g2.drawImage(image, 0, 0, this);
    }
    /**
     * 
     * @param n Recibe un numero n de un mouseWheelMoved para redimensionar la cuadricula
     */
    private void changeDrawableSpace(int n){
        if (n>0) {
            if (rWidth<50 && rHeight<50) {
                rWidth+=1F;
                rHeight+=1F;
                repaint();
            }
        }else{
            if (rWidth>6 && rHeight>6) {
                rWidth-=1F;
                rHeight-=1F;
                repaint();
            }
        }
        paintGrid = rWidth<20 || rHeight<20 ? true:false;
    }
    /**
     * 
     * @param x Recibe un numero flotante de coordenada X para convertirlo en su representacion fisica
     * @return El numero enviado basado en el pixelSize en su coordenada fisica
     */
    private int toFisicX(float x){
        return Math.round(centerX+x/pixelSize);
    }
    /**
     * 
     * @param y Recibe un numero flotante de coordenada Y para convertirlo en su representacion fisica
     * @return El numero enviado basado en el pixelSize en su coordenada fisica
     */
    private int toFisicY(float y){
        return Math.round(centerY-y/pixelSize);
    }
    /**
     * 
     * @param x Recibe un numero en coordenada fisica X
     * @return El numero en coordenada real, tomando en cuenta rWidth
     */
    private float toRealX(int x){
        return (x-centerX) * pixelSize;
    }
    /**
     * 
     * @param y Recibe un numero en coordenada fisica Y
     * @return El numero en coordenada real, tomando en cuenta rHeight
     */
    private float toRealY(int y){
        return (centerY-y) *pixelSize;
    }
    
}
